//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<tagtext.cpp>
///	@path	~/src/gui/text/
///	@date	2008/07/11
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "lib/memory/fixed_space.h"

#include "gui/style/tagtext.h"

namespace xeres {

	// TextStyle
	TagTextBase::TextStyle::TextStyle( void )
		: m_font( StyleFont::CreateFont() )
		, m_textColor( 0xffffffff )
		, m_shadowColor( 0xff000000 )
		, m_underline( false )
		, m_strikeout( false )
		, m_shine( false )
		, m_shake( false )
	{
		// Creates default text style.
	}
	// TextStyle
	TagTextBase::TextStyle::TextStyle( const TextStyle& rhs )
		: m_font( rhs.m_font )
		, m_textColor( rhs.m_textColor )
		, m_shadowColor( rhs.m_shadowColor )
		, m_underline( rhs.m_underline )
		, m_strikeout( rhs.m_strikeout )
		, m_shine( rhs.m_shine )
		, m_shake( rhs.m_shake )
	{
		// Copies text style.
	}
	// SetDefault
	void TagTextBase::TextStyle::SetDefault( void )
	{
		m_font = StyleFont::CreateFont();
		m_textColor = ARGB( 0xffffffff );
		m_shadowColor = ARGB( 0xff000000 );
		m_underline = false;
		m_strikeout = false;
		m_shine = false;
		m_shake = false;
	}
	// operator==
	bool TagTextBase::TextStyle::operator == ( const TextStyle& rhs ) const
	{
		if( m_font != rhs.m_font ) return false;
		if( m_textColor.argb != rhs.m_textColor.argb ) return false;
		if( m_shadowColor.argb != rhs.m_shadowColor.argb ) return false;
		if( m_underline != rhs.m_underline ) return false;
		if( m_strikeout != rhs.m_strikeout ) return false;
		if( m_shake != rhs.m_shake ) return false;
		return true;
	}

	// Tag
	TagTextBase::Tag::Tag( void )
		: m_prev( NULL )
		, m_next( NULL )
		, m_line( NULL )
		, m_start( 0 )
		, m_width( 0 )
		, m_height( 0 )
		, m_text()
		, m_time( 0 )
		, m_frameIndex( 0 )
	{
	}
	// ~Tag
	TagTextBase::Tag::~Tag( void )
	{

	}
	// Clear
	void TagTextBase::Tag::Clear( void )
	{
		m_prev = m_next = NULL;
		m_line = NULL;
		m_start = m_width = m_height = 0;
		m_text.clear();
		m_style.SetDefault();
		m_style.m_font = NULL;
		m_icon = NULL;
	}
	// SetText
	void TagTextBase::Tag::SetText( const WString& text )
	{
		if( m_icon.IsValid() )
		{
			FATAL_ABORT( _S("TagTextBase::Tag: Icon of tag is set before text.") );
		}
		m_text = text;
		// text width & height
		{
			Uniscribe uni( m_style.m_font->hfont() );
			uni.SetText( text );
			int x = -1;
			uni.CPtoX( text.size() - 1 , TRUE , &x );
			m_width = x;
			m_height = m_style.m_font->GetFontHeight();
		}
	}
	// SetIcon
	void TagTextBase::Tag::SetIcon( RefWeak<IBitmap> icon )
	{
		if( !m_text.empty() )
		{
			FATAL_ABORT( _S("TagTextBase::Tag: Text of tag is set before icon.") );
		}
		m_icon = icon;
		// icon width & height
		{
			m_width = icon->GetWidth();
			m_height = icon->GetHeight();
			m_time = 0;
			m_frameIndex = 0;
		}
	}
	// IsTickable
	bool TagTextBase::Tag::IsTickable( void )
	{
		if( IsText() )
		{
			return m_style.m_shake || m_style.m_shine;
		}
		else
		{
			return m_icon->GetFrame() > 0;
		}
	}
	// Tick
	void TagTextBase::Tag::Tick( Updater& updater , float dTime , Rect<int>& region )
	{
		if( IsText() )
		{
			// Update text animation.
		}
		else
		{
			// Update icon sprite.
			float delay = m_icon->GetDelay( m_frameIndex );

			m_time += dTime;
			if( m_time < delay )
			{
				return;
			}

			do
			{
				m_time -= delay;
				++m_frameIndex;
				if( m_frameIndex >= m_icon->GetFrame() )
				{
					m_frameIndex = 0;
				}
				delay = m_icon->GetDelay( m_frameIndex );
			}while( m_time > delay );

			if( m_time < 0 )
				m_time = 0;

			Rect<int> rc = make_rect<int>( m_start , m_line->m_base , m_icon->GetWidth() , m_icon->GetHeight() );
			region.union_rect( rc );
		}
	}
	// Draw
	void TagTextBase::Tag::Draw( Composition& composition , const Rect<int>& bound )
	{
		// Dispatch!
		if( IsText() )
		{
			size_t base = m_line->m_base + ( m_line->m_height - ( m_line->m_descent + m_style.m_font->GetAscent() ) );

			Rect<int> rc = make_rect( (int)m_start , (int)base , (int)GetWidth() , (int)GetHeight() );
			// Draw text.
			composition.painter().DrawShadowText(
				m_style.m_textColor , m_style.m_shadowColor , m_style.m_font ,
				m_text.c_str() , -1 ,
				rc , bound ,
				DT_SINGLELINE | DT_LEFT | DT_BOTTOM ,
				1 , 1 );

			if( m_style.m_underline )
			{
				size_t baseline = m_line->m_base + ( m_line->m_height - ( m_line->m_descent ) ) + 2;
				Gdiplus::Color c( m_style.m_textColor.a , m_style.m_textColor.r , m_style.m_textColor.g , m_style.m_textColor.b );
				Gdiplus::Pen p( c );
				composition.graphics().DrawLine( &p , (int)m_start , (int)baseline , (int)m_start + (int)GetWidth() , (int)baseline );
			}
		}
		else
		{
			size_t base = m_line->m_base + ( m_line->m_height - ( m_line->m_descent + GetHeight() ) );

			Pair<int> pos( m_start , base );

			// Draw icon.
			composition.painter().DrawBitmap( m_icon , pos , bound , 255 , m_frameIndex );
		}
	}
	// Line
	TagTextBase::Line::Line( void )
		: m_leading( NULL )
		, m_ending( NULL )
		, m_base( 0 )
		, m_height( 0 )
		, m_width( 0 )
		, m_descent( 0 )
	{

	}
	// Line
	TagTextBase::Line::Line( const Line& rhs )
		: m_leading( rhs.m_leading )
		, m_ending( rhs.m_ending )
		, m_base( rhs.m_base )
		, m_height( rhs.m_height )
		, m_width( rhs.m_width )
		, m_descent( rhs.m_descent )
	{
	}
	// AppendTag
	void TagTextBase::Line::AppendTag( Tag * tag , Pair<int>& pos )
	{
		if( m_leading == NULL )
		{
			m_leading = tag;
		}
		else
		{
			m_ending->m_next = tag;
			tag->m_prev = m_ending;
		}
		m_ending = tag;
		m_height = max( m_height , tag->GetHeight() );
		m_width += tag->GetWidth();

		if( tag->IsText() )
		{
			m_descent = max( (int)m_descent , (int)(tag->m_style.m_font->GetDescent()) );
		}

		pos.x += tag->GetWidth();
	}
	// NewLine
	void TagTextBase::Line::NewLine( void )
	{
		m_leading = m_ending = NULL;
		m_base = 0;
		m_height = 0;
		m_width = 0;
		m_descent = 0;
	}
	// Draw
	void TagTextBase::Line::Draw( Composition& composition , const Rect<int>& bound )
	{
		Tag * tag = m_leading;
		while( tag )
		{
			tag->Draw( composition , bound );
			tag = tag->m_next;
		}
	}
	// CompleteLine
	void TagTextBase::Line::CompleteLine( void )
	{
		Tag * tag = m_leading;
		m_height = 0;
		while( tag )
		{
			if( tag->IsText() )
			{
				m_height = max( tag->GetHeight() , m_height );
			}
			else
			{
				size_t real_height = tag->GetHeight() + m_descent;
				m_height = max( real_height , m_height );
			}
			
			tag->m_line = this;
			tag = tag->m_next;
		}
	}

	namespace
	{
		// A pool for Tags.
		class TagPool
		{
		public:

			/// \instance
			static TagPool& instance( void )
			{
				static TagPool s_TagPool;
				return s_TagPool;
			}

			/// \brief	Get free tag instance.
			TagTextBase::Tag * GetTag( void )
			{
				return new ( m_space.allocate() ) TagTextBase::Tag;
			}

			/// \brief	Return unused tag.
			void FreeTag( TagTextBase::Tag * tag )
			{
				tag->~Tag();
				m_space.deallocate( tag );
			}

		private:

			/// \ctor
			TagPool( void )
			{
			}

			/// \dtor
			~TagPool( void )
			{
			}

			//@ data
			fixed_space< sizeof(TagTextBase::Tag) > m_space;
		};
	}

	// AllocTag
	TagTextBase::Tag * TagTextBase::AllocTag( void )
	{
		return TagPool::instance().GetTag();
	}
	// FreeTag
	void TagTextBase::FreeTag( Tag * tag )
	{
		TagPool::instance().FreeTag( tag );
	}

} // namespace xeres
